/**
 * 工具
 */

import md5Encrypt from 'js-md5'
import store from '@/store'
import defaultSettings from '@/settings'
import JsEncrypt from 'jsencrypt'
import NP from 'number-precision'

/**
 * @param {string} path
 * @returns {Boolean}
 */
export const isExternal = (path) => {
  return /^(https?:|mailto:|tel:)/.test(path)
}

/**
 * 将一个对象数组的某个字符取出并join
 * @param objArr 对象数组
 * @param attr 字段
 * @param separator 分割符
 * @param prependIndex 是否在每项前加下标
 */
export const joinFromObjArr = (objArr, attr, separator, prependIndex, prependAnother) => {
  return objArr.map((obj, index) => {
    if (prependIndex) {
      return (index + 1) + prependAnother + obj[attr]
    } else {
      return obj[attr]
    }
  }).join(separator)
}

/**
 * 将数值格式化成最大不超过多少位小数的数值，非四舍五入，而是用substring来做的
 * 如setMaxScale(1.289, 2)结果是1.28，而不是1.29
 * @param value 要转换的数值
 * @param maxScale 最大保留几位小数，可以不传
 * @param padAfter 是否自动补齐0, 2 -> 2.00
 * @returns {*}
 */
export const formatMaxScale = (value, maxScale, padAfter = false) => {
  if (value === undefined || value === null) {
    return ''
  }
  if (!maxScale) {
    maxScale = defaultSettings.defaultMaxScale
  }
  let valStr = value.toString()
  if (value === 0) {
    if (padAfter) {
      let scaleCount = maxScale
      valStr += '.'
      while (scaleCount--) {
        valStr += '0'
      }
      return valStr
    }
    return 0
  }
  const nums = valStr.split('.')
  if (nums.length === 1) {
    if (padAfter) {
      let scaleCount = maxScale
      valStr += '.'
      while (scaleCount--) {
        valStr += '0'
      }
      return valStr
    }
    return value
  }
  if (nums.length > 1) {
    if (nums[1].length > maxScale) {
      const end = nums[0].length + 1 + maxScale
      return padAfter ? valStr.substring(0, end) : parseFloat(valStr.substring(0, end))
    }
    if (padAfter) {
      let scaleCount = maxScale - nums[1].length
      while (scaleCount--) {
        valStr += '0'
      }
      return valStr
    }
    return value
  }
  return padAfter ? valStr : value
}

/**
 * 10000 => "10,000"
 * @param value 要转换的数值
 * @param maxScale 保留小数倍数，可以不传
 * @param padAfter 最大保留几位小数，可以不传
 * @return {string}
 */
export const formatToThousand = (value, maxScale, padAfter = false) => {
  if (!isNumber(value)) return ''
  const scaleNum = formatMaxScale(value, maxScale, false)
  return scaleNum.toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

/**
 * 加法函数，用来得到精确的加法结果
 * 说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
 * @param arg1 数值1
 * @param arg2 数值2
 * @returns {number}
 */
export const numberAdd = (arg1, arg2) => {
  return NP.plus(arg1, arg2)
}

/**
 * 减法函数，用来得到精确的加法结果
 * 说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
 * @param arg1 数值1
 * @param arg2 数值2
 * @returns {number}
 */
export const numberSub = (arg1, arg2) => {
  if (arg1 === arg2) {
    return 0
  }
  return NP.minus(arg1, arg2)
}

/**
 * 除法函数，用来得到精确的除法结果
 * 说明：javascript的算法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的除法结果。
 * @param arg1 数值1
 * @param arg2 数值2
 * @returns {number}
 */
export const numberDiv = (arg1, arg2) => {
  return NP.divide(arg1, arg2)
}

/**
 * 乘法函数，用来得到精确的乘法结果
 * 说明：javascript的算法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的乘法结果。
 * @param arg1 数值1
 * @param arg2 数值2
 * @returns {number}
 */
export const numberMul = (arg1, arg2) => {
  if (!arg1) {
    arg1 = 0
  }
  if (!arg2) {
    arg2 = 0
  }
  return NP.times(arg1, arg2)
}

/**
 * 生成服务器传输使用的密码
 * @param plainString 密码明文
 * @returns {string}
 */
export const generateServerPwd = (plainString) => {
  return md5Encrypt(plainString)
}

/**
 * 生成一个新的后台能识别的密码，用于创建类的操作（如修改登录密码，用来加密新的登录密码，调用此方法，将返回值直接传给后台）
 * @param plainStr
 * @returns {*}
 */
export const generatePassword = plainStr => {
  return md5Encrypt(plainStr)
}

/**
 * 查看对象是否是空
 * 1：null
 * 2：undefined
 * 3：空格字符串
 * 4：空字符串
 * @param val 要判断的字符串
 * @returns {boolean}
 */
export const isEmpty = val => {
  if (val == null || val === undefined || /^\s+$/.test(val) || val.length < 1) {
    return true
  }
  return false
}

/**
 * 查看对象是否是空
 * 1：null
 * 2：undefined
 * 3：空格字符串
 * 4：空字符串
 * @param val 要判断的字符串
 * @returns {boolean}
 */
export const isNotEmpty = val => {
  return !isEmpty(val)
}

/**
 * 判断集合是否为null或length小于等于0
 * @param list 集合
 * @returns {boolean}
 */
export const isEmptyArray = list => {
  return !list || list.length <= 0
}

/**
 * 判断集合是否不为null并且length大于0
 * @param list 集合
 * @returns {boolean}
 */
export const isNotEmptyArray = list => {
  return !isEmptyArray(list)
}

/**
 * 判断对象是否是一个方法
 * @param func 方法
 * @returns {boolean}
 */
export const isFunction = func => {
  return typeof func === 'function'
}

/**
 * 是否是object
 * @param obj 是否是object
 * @returns {boolean}
 */
export const isObject = obj => {
  return obj !== null && typeof obj === 'object'
}

/**
 * 是否是一个promise
 * @param val
 * @returns {*|boolean}
 */
export const isPromise = val => {
  return val && typeof val.then === 'function'
}

/**
 * 判断是否是数组
 * @param value 数值
 * @returns {boolean}
 */
export const isArray = (value) => {
  return value && value.constructor === Array
}

export const isString = (value) => {
  return typeof value === 'string'
}

export const inArray = (arr, findValue) => {
  const find = arr.find(value => value === findValue)
  return !!find
}

/**
 * 验证字符串是否是合法的用户名，2-64位，首字母（前后台一样）
 * @param value 用户名
 * @returns {boolean}
 */
export const isUserName = (value) => {
  return /[a-zA-Z0-9]{2,64}/.test(value)
}

/**
 * 验证是否是密码，密码必须为英文、数字、字符任意两种组合，长度6-20位
 * @param value
 * @returns {boolean}
 */
export const isPassword = (value) => {
  // 下面这条在ESLint不会报错
  return /^(?![\d]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*()_+`\-={}:;<>?,.]+$)[\da-zA-Z~!@#$%^&*()_+`\-={}:;<>?,.]{6,20}$/.test(value)

  // 下面的正则在ESLint中会报错，如果有此情况，要求直接关闭ESLint，关闭方法https://blog.csdn.net/qq_36888550/article/details/79815195
  // return /^(?![\d]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*()_+`\-={}\[\]:;<>?,.\/]+$)[\da-zA-Z~!@#$%^&*()_+`\-={}\[\]:;<>?,.\/]{6,20}$/.test(value);
}

/**
 * 根据返回的状态进行相应提示，也可以认为是统一处理返回的数据，当未登录时，则会自动跳到登录页面，同时也会清除store的登录状态
 * @param errorRsp 异常对象
 * @param router 路由对象
 */
export const assertRsp = rsp => {
  // const msg = errorRsp.message
  // let redirectPath = null
  // if (isNotEmpty(errorRsp.data) && isNotEmpty(errorRsp.data.code)) {
  //   if (isLogoutCode(errorRsp.data.code)) {
  //     store.commit(LOGOUT)
  //     redirectPath = '/login'
  //   }
  // } else if (isNotEmpty(errorRsp.code)) {
  //   if (isLogoutCode(errorRsp.code)) {
  //     store.commit(LOGOUT)
  //     redirectPath = '/login'
  //   }
  // }
  //
  // if (redirectPath) {
  //   Message.warningAutoClose(msg).then(() => router.push(redirectPath))
  // } else {
  //   Message.warning(msg)
  // }
}

/**
 * 断言，根据断言条件，不成立时抛出错误
 * @param condition 条件
 * @param msg 抛错信息
 */
export const assert = (condition, msg) => {
  if (!condition) throw new Error(`${msg}`)
}

/**
 * 断言字符串或其它类型值不能为空
 * @param val 值
 * @param msg 为空时抛出的错误
 */
export const assertNotEmpty = (val, msg) => {
  if (isEmpty(val)) {
    throw new Error(`${msg}`)
  }
}

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array<Object>} cache
 * @return {*}
 */
export const deepCopy = (obj, cache = []) => {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

/**
 * forEach for object
 */
export const forEachValue = (obj, fn) => {
  Object.keys(obj).forEach(key => fn(key, obj[key]))
}

/**
 * 判断是否是数值，int,float都算
 * @param value 数值
 * @returns {boolean}
 */
export const isNumber = (value) => {
  return !isNaN(value)
}

/**
 * 判断是否是数值数组字符串(100,200,500,100)，int,float都算
 * @param value 数值
 * @returns {boolean}
 */
export const isNumberRangeStr = (value) => {
  if (isNumber(value)) {
    return true
  }
  const valueArr = value.toString().split(',')
  return valueArr.every(val => isNumber(val))
}

/**
 * 判断是否不是数值
 * @param value 数值
 * @returns {boolean}
 */
export const isNotNumber = (value) => {
  return !isNumber(value)
}

/**
 * 判断是否是整数
 * @param value 数值
 * @returns {boolean}
 */
export const isDigits = (value) => {
  const reg = /^[0-9]+$/
  return reg.test(value)
}

/**
 * 判断是否是正整数
 * @param value 数值
 * @returns {boolean}
 */
export const isDigitsZZS = (value) => {
  return isDigits(value) && parseInt(value) >= 0
}

/**
 * 判断0-9以内是否是质数
 * @param value 数值
 * @returns {boolean}
 */
export const isPrimeNum = (value) => {
  if (!isDigits(value)) {
    return false
  }
  // 注意：为了高性能，这里只作0-9以内的判断，不可用作实际算法 1、2、3、5、7
  const num = Number(value)
  if (num === 1 || num === 2 || num === 3 || num === 5 || num === 7) {
    return true
  }
  return false
}

/**
 * 判断0-9以内是否是合数
 * @param value 数值
 * @returns {boolean}
 */
export const isCompositeNum = (value) => {
  if (!isDigits(value)) {
    return false
  }
  // 注意：为了高性能，这里只作0-9以内的判断，不可用作实际算法 0、4、6、8、9
  const num = Number(value)
  if (num === 0 || num === 4 || num === 6 || num === 8 || num === 9) {
    return true
  }
  return false
}

/**
 * 检测是不是正浮点数
 * @param value
 * @returns {boolean}
 */
export const isPositiveNumber = (value) => {
  const reg = /^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$/
  return reg.test(value)
}

/**
 * 存储localStorage
 * @param name 项名
 * @param content 项值
 */
export const setStorage = (name, content) => {
  if (!name) return
  window.localStorage.setItem(name, content)
}

/**
 * 获取localStorage
 * @param name 项名
 */
export const getStorage = name => {
  if (!name) return
  return window.localStorage.getItem(name)
}

/**
 * 删除localStorage
 * @param name 项名
 */
export const removeStorage = name => {
  if (!name) return
  window.localStorage.removeItem(name)
}

/**
 * 定义一个空的function
 * @returns {Function}
 */
export const emptyFn = () => {
  return () => {}
}

/**
 * 查看是否为空对象
 * {  }
 */
export function isEmptyObject(obj) {
  return !Object.getOwnPropertyNames(obj).length && !Object.getOwnPropertySymbols(obj).length
}

/**
 * 清空对象的值
 * @param obj
 */
export const clearObject = obj => {
  Object.keys(obj).forEach(key => {
    const innerObj = obj[key]
    isObject(innerObj) ? clearObject(innerObj) : obj[key] = ''
  })
}

/**
 * 清除定时任务ID
 * clearTimeout & clearInterval & timerId = null
 * @param timerId
 */
export const clearTimer = (timerId) => {
  if (timerId) {
    clearTimeout(timerId)
    clearInterval(timerId)
    timerId = null
  }
}

/**
 * 将string数组求和
 * @param strArr string数组
 * @returns {number}
 */
export const getStrArrSum = (strArr) => {
  const intArr = formatStrArrToInt(strArr)
  return getIntArrSum(intArr)
}

/**
 * 将int数组求和
 * @param intArr int数组
 * @returns {number}
 */
export const getIntArrSum = (intArr) => {
  let sum = 0
  for (let i = 0; i < intArr.length; i++) {
    sum += intArr[i]
  }
  return sum
}

/**
 * 将string数组转为int数组
 * @param strArr string数组
 * @returns {Array}
 */
export const formatStrArrToInt = (strArr) => {
  const codesIntArr = []
  for (let i = 0; i < strArr.length; i++) {
    codesIntArr.push(parseInt(strArr[i]))
  }
  return codesIntArr
}

export const formatPlaceholderByName = (template, data) => {
  if (!isString(template)) {
    return data
  }
  let result = template
  forEachValue(data, (key, val) => {
    result = result.replace(new RegExp('{' + key + '}', 'g'), val)
  })
  return result
}

export const formatPlaceholderByNoName = function(template, ...argArr) {
  if (!argArr) {
    return template
  }
  let argIndex = 0
  return template.replace(/\{\}/g, (temp, tempIndex) => {
    if (argArr[argIndex]) {
      return argArr[argIndex++]
    }
    argIndex++
    return ''
  })
}

/**
 * 当字符数量不及时，给字符前面补0
 * @param value 字符串
 * @param count 期望有多少个字符
 */
export const patchZeroFront = (value, count) => {
  const strValue = value + ''
  const lessCount = count - strValue.length
  if (lessCount <= 0) {
    return value
  }

  let patchValue = strValue
  for (let i = 0; i < lessCount; i++) {
    patchValue = ('0' + patchValue)
  }
  return patchValue
}

/**
 * 重复字符，返回字符串
 * @param value 字符串
 * @param repeatTimes 重复多少次
 */
export const repeatCharacter = (value, repeatTimes) => {
  let strValue = value + ''
  for (let i = 0; i < repeatTimes; i++) {
    strValue += strValue
  }
  return strValue
}

/**
 * 数组从小到大排序，必须保证所有元素都是整型
 * @param arr int数组
 */
export const quickSortIntAsc = (arr) => {
  // 如果数组长度小于等于1，则返回数组本身
  if (arr.length <= 1) {
    return arr
  }
  // 定义中间值的索引
  const index = Math.floor(arr.length / 2)
  // 取到中间值
  const temp = parseInt(arr.splice(index, 1))
  // 定义左右部分数组
  const left = []
  const right = []
  for (let i = 0; i < arr.length; i++) {
    // 如果元素比中间值小，那么放在左边，否则放右边
    if (parseInt(arr[i]) < temp) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }
  return quickSortIntAsc(left).concat(temp, quickSortIntAsc(right))
}

/**
 * 组合数计算方法，从N项中选出M项.
 * @param n N
 * @param m M
 * @returns {*}
 */
export const comNum = (n, m) => {
  m = parseInt(m)
  n = parseInt(n)
  if (m < 0 || n < 0) {
    return false
  }
  if (m === 0 || n === 0) {
    return 1
  }
  if (m > n) {
    return 0
  }
  if (m > n / 2.0) {
    m = n - m
  }
  let result = 0.0
  for (let i = n; i >= (n - m + 1); i--) {
    result += Math.log(i)
  }
  for (let i = m; i >= 1; i--) {
    result -= Math.log(i)
  }
  result = Math.exp(result)
  return Math.round(result)
}

/**
 * @param {string} url
 * @returns {Object}
 */
export function getQueryObject(url) {
  url = url == null ? window.location.href : url
  const search = url.substring(url.lastIndexOf('?') + 1)
  const obj = {}
  const reg = /([^?&=]+)=([^?&=]*)/g
  search.replace(reg, (rs, $1, $2) => {
    const name = decodeURIComponent($1)
    let val = decodeURIComponent($2)
    val = String(val)
    obj[name] = val
    return rs
  })
  return obj
}

/**
 * @param {Function} func
 * @param {number} wait
 * @param {boolean} immediate
 * @return {*}
 */
export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function() {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

  return function(...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在，重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }

    return result
  }
}

/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

/**
 * @param {Array} arr
 * @returns {Array}
 */
export function uniqueArr(arr) {
  return Array.from(new Set(arr))
}

/**
 * @returns {string}
 */
export function createUniqueString() {
  const timestamp = +new Date() + ''
  const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  return (+(randomNum + timestamp)).toString(32)
}

/**
 * Check if an element has a class
 * @param {HTMLElement} elm
 * @param {string} cls
 * @returns {boolean}
 */
export function hasClass(ele, cls) {
  return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}

/**
 * Add class to element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function addClass(ele, cls) {
  if (!hasClass(ele, cls)) ele.className += ' ' + cls
}

/**
 * Remove class from element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function removeClass(ele, cls) {
  if (hasClass(ele, cls)) {
    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
    ele.className = ele.className.replace(reg, ' ')
  }
}

/**
 * @param {HTMLElement} element
 * @param {string} className
 */
export function toggleClass(element, className) {
  if (!element || !className) {
    return
  }
  let classString = element.className
  const nameIndex = classString.indexOf(className)
  if (nameIndex === -1) {
    classString += '' + className
  } else {
    classString =
      classString.substr(0, nameIndex) +
      classString.substr(nameIndex + className.length)
  }
  element.className = classString
}

/**
 * @param {string} val
 * @returns {string}
 */
export function html2Text(val) {
  const div = document.createElement('div')
  div.innerHTML = val
  return div.textContent || div.innerText
}

/**
 * 对数组object的数字字段降序排序
 * @param propertyName
 * @returns {function(*, *): number}
 * @param type asc|desc，默认asc
 */
export function numberCompare(propertyName, type) {
  const _type = type || 'asc'
  if (_type === 'asc') {
    return function(object1, object2) {
      const value1 = object1[propertyName]
      const value2 = object2[propertyName]
      return value2 < value1 ? 1 : value2 > value1 ? -1 : 0
    }
  } else {
    return function(object1, object2) {
      const value1 = object1[propertyName]
      const value2 = object2[propertyName]
      return value2 > value1 ? 1 : value2 < value1 ? -1 : 0
    }
  }
}

/**
 * 对数组object的字符字段降序排序
 * @param propertyName
 * @returns {function(*, *): number}
 * @param type asc|desc，默认asc
 */
export function stringCompare(propertyName, type) {
  const _type = type || 'asc'
  if (_type === 'asc') {
    return function(object1, object2) {
      const value1 = object1[propertyName]
      const value2 = object2[propertyName]
      return value1.localeCompare(value2)
    }
  } else {
    return function(object1, object2) {
      const value1 = object1[propertyName]
      const value2 = object2[propertyName]
      return value2.localeCompare(value1)
    }
  }
}

export const isIpv4 = (value) => {
  const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
  return reg.test(value)
}

export const isIpv4s = (value) => {
  // 字符串为空或者空串
  if (!value) {
    return false
  }

  const ips = value.split(',')
  if (ips.length <= 0) {
    return false
  }

  return !ips.some(ip => !isIpv4(ip))
}

/**
 * 行转列
 * @param rows 行列表
 * @param rowKeyName 行的key列名
 * @param rowValueName 行的value列名
 * @returns {{}}
 */
export const rowsToColumn = (rows, rowKeyName, rowValueName) => {
  const column = {}
  rows.forEach(row => {
    column[row[rowKeyName]] = row[rowValueName]
  })
  return column
}

/**
 * 列转行
 * @param column 列
 * @param rowKeyName 行的key列名
 * @param rowValueName 行的value列名
 * @returns {{}}
 */
export const columnToRows = (column, rowKeyName, rowValueName) => {
  const rows = []
  forEachValue(column, (key, value) => {
    const row = {}
    row[rowKeyName] = key
    row[rowValueName] = value
    rows.push(row)
  })
  return rows
}

/**
 * 将指定日期转换为yyyy-MM-dd
 * @param date
 * @returns {string}
 */
export const formatDateToDateStr = date => {
  if (!date) {
    date = new Date()
  }
  const year = date.getFullYear()
  const month = patchZeroFront(date.getMonth() + 1, 2)
  const day = patchZeroFront(date.getDate(), 2)
  return `${year}-${month}-${day}`
}
/**
 * 将明天转换为yyyy-MM-dd
 * @param date
 * @returns {string}
 */
export const formatTomorrowToDateStr = () => {
  const date = new Date()
  date.setTime(date.getTime() + 24 * 60 * 60 * 1000)
  const year = date.getFullYear()
  const month = patchZeroFront(date.getMonth() + 1, 2)
  const day = patchZeroFront(date.getDate(), 2)
  return `${year}-${month}-${day}`
}

/**
 * 将指定日期转换为yyyy-MM-dd HH:mm:ss
 * @param date
 * @returns {string}
 */
export const formatDateToDateTimeStr = date => {
  const year = date.getFullYear()
  const month = (date.getMonth() + 1).toString().padStart(2, '0')
  const day = date.getDate().toString().padStart(2, '0')
  const hour = date.getHours().toString().padStart(2, '0')
  const minute = date.getMinutes().toString().padStart(2, '0')
  const second = date.getSeconds().toString().padStart(2, '0')
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}

/**
 * 获取今天日期范围，不包含时间[今天yyyy-MM-dd, 今天yyyy-MM-dd]
 * 会按照偏移时间进行计算
 */
export const offsetDateRangeStr_today = () => {
  const date = getOffsetStartDate(0)
  return [formatDateToDateStr(date), formatDateToDateStr(date)]
}

/**
 * 获取最近7天范围，不包含时间[今天yyyy-MM-dd, 今天yyyy-MM-dd]
 * 会按照偏移时间进行计算
 */
export const offsetDateRangeStr_lastSevenDays = () => {
  const start = getOffsetStartDate(-7)
  const end = getOffsetStartDate(0)
  return [formatDateToDateStr(start), formatDateToDateStr(end)]
}

/**
 * 获取本月范围，不包含时间[本月开始yyyy-MM-dd, 本月结束yyyy-MM-dd]
 * 会按照偏移时间进行计算
 */
export const offsetDateRangeStr_thisMonth = () => {
  const start = getOffsetStartDate(0)
  // 这个月第1天
  start.setDate(1)
  const end = getOffsetStartDate(0)
  // // 这个月最后一天
  // end.setMonth(end.getMonth() + 1)
  // end.setDate(0)
  return [formatDateToDateStr(start), formatDateToDateStr(end)]
}

/**
 * 获取今天时间范围，[今天yyyy-MM-dd HH:mm:ss, 今天yyyy-MM-dd HH:mm:ss]
 * 如偏移时间为03:00:00，当前时间(浏览器时间)
 * 小于03:00:00，则昨天03:00:00~今天02:59:59，大于等于则今天03:00:00~明天02:59:59
 * @return {[string, string]}
 */
export const offsetDateTimeRangeStr_today = () => {
  const start = getOffsetStartDate(0)
  const end = getOffsetEndDate(0)
  return [formatDateToDateTimeStr(start), formatDateToDateTimeStr(end)]
}

/**
 * 获取最近7时间范围，[今天yyyy-MM-dd HH:mm:ss, 今天yyyy-MM-dd HH:mm:ss]
 * 如偏移时间为03:00:00，当前时间(浏览器时间)
 * 小于03:00:00，则昨天03:00:00~今天02:59:59，大于等于则今天03:00:00~明天02:59:59
 * @return {[string, string]}
 */
export const offsetDateTimeRangeStr_lastSevenDays = () => {
  const start = getOffsetStartDate(-7)
  const end = getOffsetEndDate(0)
  return [formatDateToDateTimeStr(start), formatDateToDateTimeStr(end)]
}

/**
 * 获取最近1月范围，[今天yyyy-MM-dd HH:mm:ss, 今天yyyy-MM-dd HH:mm:ss]
 * 如偏移时间为03:00:00，当前时间(浏览器时间)
 * 小于03:00:00，则昨天03:00:00~今天02:59:59，大于等于则今天03:00:00~明天02:59:59
 * @return {[string, string]}
 */
export const offsetDateTimeRangeStr_lastOneMonth = () => {
  const start = getOffsetStartDate(0)
  start.setMonth(start.getMonth() - 1)
  const end = getOffsetEndDate(0)
  return [formatDateToDateTimeStr(start), formatDateToDateTimeStr(end)]
}

/**
 * 获取最近3月范围，[今天yyyy-MM-dd HH:mm:ss, 今天yyyy-MM-dd HH:mm:ss]
 * 如偏移时间为03:00:00，当前时间(浏览器时间)
 * 小于03:00:00，则昨天03:00:00~今天02:59:59，大于等于则今天03:00:00~明天02:59:59
 * @return {[string, string]}
 */
export const offsetDateTimeRangeStr_lastThreeMonth = () => {
  const start = getOffsetStartDate(0)
  start.setMonth(start.getMonth() - 3)
  const end = getOffsetEndDate(0)
  return [formatDateToDateTimeStr(start), formatDateToDateTimeStr(end)]
}

/**
 * 获取开始时间对象，如偏移时间为03:00:00，当前时间(浏览器时间)小于03:00:00，则昨天03:00:00，大于等于则今天03:00:00
 * @param addDay 增减天数
 * @returns {Date}
 */
export const getOffsetStartDate = (addDay) => {
  const date = new Date()
  if (addDay) {
    date.setDate(date.getDate() + addDay)
  }

  // 统一设置毫秒数
  date.setMilliseconds(0)

  return date
}

/**
 * 获取结束时间对象，如偏移时间为03:00:00，当前时间(浏览器时间)小于03:00:00，则今天02:59:59，大于等于则明天02:59:59
 * @param addDay 增减天数
 * @returns {Date}
 */
export const getOffsetEndDate = (addDay) => {
  const date = new Date()
  if (addDay) {
    date.setDate(date.getDate() + addDay)
  }

  // 统一设置毫秒数
  date.setMilliseconds(999)

  adjustOffsetHMS(date)
  date.setSeconds(date.getSeconds() - 1)
  if (!isNowLowerThanReportOffsetTime()) {
    date.setDate(date.getDate() + 1)
  }

  return date
}

/**
 * 将报表偏移时间设置到时分秒里面去
 * @param date 待偏移Date对象
 */
export const adjustOffsetHMS = (date) => {
  const offsetTime = store.getters.serverConfig.REPORT.offsetTime
  if (!isTime(offsetTime)) {
    return date
  }

  const offsetTimeArr = offsetTime.split(':')
  const offsetHour = parseInt(offsetTimeArr[0])
  const offsetMinute = parseInt(offsetTimeArr[1])
  const offsetSecond = parseInt(offsetTimeArr[2])

  date.setHours(offsetHour)
  date.setMinutes(offsetMinute)
  date.setSeconds(offsetSecond)

  return date
}

export const isTime = (str) => {
  const exp = str.match(/^(\d{1,2})(:)?(\d{1,2})\2(\d{1,2})$/)
  if (exp == null) {
    return false
  }
  if (exp[1] > 24 || exp[3] > 60 || exp[4] > 60) {
    return false
  }
  return true
}

/**
 * 当前时间是否小于报表偏移时间
 */
export const isNowLowerThanReportOffsetTime = () => {
  const offsetTime = store.getters.serverConfig.REPORT.offsetTime
  return isNowLowerThanTimeHMS(offsetTime)
}

export const isNowLowerThanTimeHMS = (timeHMS) => {
  if (!isTime(timeHMS)) {
    return false
  }

  // 当前时间
  const now = new Date()
  const nowHour = now.getHours()
  const nowMinute = now.getMinutes()
  const nowSecond = now.getSeconds()

  const timeArr = timeHMS.split(':')
  const timeHour = parseInt(timeArr[0])
  const timeMinute = parseInt(timeArr[1])
  const timeSecond = parseInt(timeArr[2])

  if (nowHour !== timeHour) {
    return nowHour < timeHour
  }
  if (nowMinute !== timeMinute) {
    return nowMinute < timeMinute
  }
  if (nowSecond !== timeSecond) {
    return nowSecond < timeSecond
  }

  return false
}

/**
 * 合并列表数据
 */
export const summaryTableData = (list) => {
  if (!list || list <= 0) {
    return
  }

  const copyList = deepClone(list)

  const singleData = list[0]

  const summaryData = {}
  summaryData.dataType = 'summary'

  forEachValue(singleData, (key, value) => {
    if (isNumber(value)) {
      summaryData[key] = list.map(d => d[key]).reduce((a, b) => numberAdd(a, b))
    } else {
      summaryData[key] = ''
    }
  })

  copyList.push(summaryData)
  return copyList
}

/**
 * 解密服务器传过来的RSA公钥加密的数据
 * @param str
 * @return {PromiseLike<ArrayBuffer>}
 */
export const rsaPrivateDecryptApiData = (str) => {
  const jse = new JsEncrypt()
  jse.setPrivateKey(defaultSettings.serverRSAPrivateKey)
  const val = jse.decrypt(str)
  return val
}

/**
 * 获取当前设备类型
 * @returns {string}
 */
export const getDeviceType = () => {
  // 1是PC
  // 2是H5
  // 管理端和代理端都是PC版的，这里就固定值
  return '1'
}

/**
 * 获取当前设备标识
 * @type {function(): string | string}
 */
export const getDeviceId = () => {
  return (window || {
    navigator: {
      userAgent: ''
    }
  }).navigator.userAgent
}

/**
 * 时间转为秒
 * @param time 时间(00:00:00)
 * @returns {number} 时间戳（单位：秒）
 */
export const timeToSec = (time) => {
  const hour = time.split(':')[0]
  const min = time.split(':')[1]
  const sec = time.split(':')[2]
  return Number(hour * 3600) + Number(min * 60) + Number(sec)
}

/**
 * 时间秒数格式化
 * @param s 时间戳（单位：秒）
 * @returns {*} 格式化后的时分秒
 */
export const secToTime = (s) => {
  let t = ''
  if (s > -1) {
    const hour = Math.floor(s / 3600)
    const min = Math.floor(s / 60) % 60
    const sec = s % 60
    if (hour < 10) {
      t = '0' + hour + ':'
    } else {
      t = hour + ':'
    }

    if (min < 10) {
      t += '0'
    }
    t += min + ':'
    if (sec < 10) {
      t += '0'
    }
    t += sec
  }
  return t
}

/**
 * 生成文件名
 * 格式 ${prefix}-yyyyMMddHHssmm
 * @param prefix 可以不传
 * @returns {string}
 */
export const generateFileName = (prefix) => {
  const date = new Date()
  const suffix = `${timePrefix(date.getFullYear())}${timePrefix(date.getMonth() + 1)}${timePrefix(date.getDate())}${timePrefix(date.getHours())}${timePrefix(date.getHours())}${timePrefix(date.getMinutes())}${timePrefix(date.getSeconds())}`
  return prefix ? `${prefix}-${suffix}` : `${suffix}`
}

/**
 * 时间小于10前面加0
 * @param time
 * @returns {string|*}
 */
function timePrefix(time) {
  if (time >= 0 && time < 10) {
    return `0${time}`
  } else {
    return time
  }
}

/**
 * 获取文件名
 * en.js => en
 * @param String
 */
export const fileNameLoad = (fileName) => {
  const length = fileName.length
  if (length < 4) {
    return ''
  } else {
    // 截取最后一个 .js 之前的内容
    return fileName.substring(0, length - 3)
  }
}
